// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/compiler/raw-machine-assembler.h"

#include "src/base/small-vector.h"
#include "src/compiler/compiler-source-position-table.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/pipeline.h"
#include "src/compiler/scheduler.h"
#include "src/heap/factory-inl.h"

namespace v8 {
namespace internal {
    namespace compiler {

        RawMachineAssembler::RawMachineAssembler(
            Isolate* isolate, Graph* graph, CallDescriptor* call_descriptor,
            MachineRepresentation word, MachineOperatorBuilder::Flags flags,
            MachineOperatorBuilder::AlignmentRequirements alignment_requirements,
            PoisoningMitigationLevel poisoning_level)
            : isolate_(isolate)
            , graph_(graph)
            , schedule_(new (zone()) Schedule(zone()))
            , source_positions_(new (zone()) SourcePositionTable(graph))
            , machine_(zone(), word, flags, alignment_requirements)
            , common_(zone())
            , simplified_(zone())
            , call_descriptor_(call_descriptor)
            , target_parameter_(nullptr)
            , parameters_(parameter_count(), zone())
            , current_block_(schedule()->start())
            , poisoning_level_(poisoning_level)
        {
            int param_count = static_cast<int>(parameter_count());
            // Add an extra input for the JSFunction parameter to the start node.
            graph->SetStart(graph->NewNode(common_.Start(param_count + 1)));
            if (call_descriptor->IsJSFunctionCall()) {
                target_parameter_ = AddNode(
                    common()->Parameter(Linkage::kJSCallClosureParamIndex), graph->start());
            }
            for (size_t i = 0; i < parameter_count(); ++i) {
                parameters_[i] = AddNode(common()->Parameter(static_cast<int>(i)), graph->start());
            }
            graph->SetEnd(graph->NewNode(common_.End(0)));
            source_positions_->AddDecorator();
        }

        void RawMachineAssembler::SetSourcePosition(const char* file, int line)
        {
            int file_id = isolate()->LookupOrAddExternallyCompiledFilename(file);
            SourcePosition p = SourcePosition::External(line, file_id);
            DCHECK(p.ExternalLine() == line);
            source_positions()->SetCurrentPosition(p);
        }

        Node* RawMachineAssembler::NullConstant()
        {
            return HeapConstant(isolate()->factory()->null_value());
        }

        Node* RawMachineAssembler::UndefinedConstant()
        {
            return HeapConstant(isolate()->factory()->undefined_value());
        }

        Node* RawMachineAssembler::RelocatableIntPtrConstant(intptr_t value,
            RelocInfo::Mode rmode)
        {
            return kSystemPointerSize == 8
                ? RelocatableInt64Constant(value, rmode)
                : RelocatableInt32Constant(static_cast<int>(value), rmode);
        }

        Node* RawMachineAssembler::OptimizedAllocate(Node* size,
            AllocationType allocation)
        {
            return AddNode(simplified()->AllocateRaw(Type::Any(), allocation), size);
        }

        Schedule* RawMachineAssembler::Export()
        {
            // Compute the correct codegen order.
            DCHECK(schedule_->rpo_order()->empty());
            if (FLAG_trace_turbo_scheduler) {
                PrintF("--- RAW SCHEDULE -------------------------------------------\n");
                StdoutStream {} << *schedule_;
            }
            schedule_->EnsureCFGWellFormedness();
            Scheduler::ComputeSpecialRPO(zone(), schedule_);
            schedule_->PropagateDeferredMark();
            if (FLAG_trace_turbo_scheduler) {
                PrintF("--- EDGE SPLIT AND PROPAGATED DEFERRED SCHEDULE ------------\n");
                StdoutStream {} << *schedule_;
            }
            // Invalidate RawMachineAssembler.
            source_positions_->RemoveDecorator();
            Schedule* schedule = schedule_;
            schedule_ = nullptr;
            return schedule;
        }

        Graph* RawMachineAssembler::ExportForOptimization()
        {
            // Compute the correct codegen order.
            DCHECK(schedule_->rpo_order()->empty());
            if (FLAG_trace_turbo_scheduler) {
                PrintF("--- RAW SCHEDULE -------------------------------------------\n");
                StdoutStream {} << *schedule_;
            }
            schedule_->EnsureCFGWellFormedness();
            Scheduler::ComputeSpecialRPO(zone(), schedule_);
            if (FLAG_trace_turbo_scheduler) {
                PrintF("--- SCHEDULE BEFORE GRAPH CREATION -------------------------\n");
                StdoutStream {} << *schedule_;
            }
            MakeReschedulable();
            // Invalidate RawMachineAssembler.
            schedule_ = nullptr;
            return graph();
        }

        void RawMachineAssembler::MakeReschedulable()
        {
            std::vector<Node*> block_final_control(schedule_->all_blocks_.size());
            std::vector<Node*> block_final_effect(schedule_->all_blocks_.size());

            struct LoopHeader {
                BasicBlock* block;
                Node* loop_node;
                Node* effect_phi;
            };
            std::vector<LoopHeader> loop_headers;

            // These are hoisted outside of the loop to avoid re-allocation.
            std::vector<Node*> merge_inputs;
            std::vector<Node*> effect_phi_inputs;

            for (BasicBlock* block : *schedule_->rpo_order()) {
                Node* current_control;
                Node* current_effect;
                if (block == schedule_->start()) {
                    current_control = current_effect = graph()->start();
                } else if (block == schedule_->end()) {
                    for (size_t i = 0; i < block->PredecessorCount(); ++i) {
                        NodeProperties::MergeControlToEnd(
                            graph(), common(), block->PredecessorAt(i)->control_input());
                    }
                } else if (block->IsLoopHeader()) {
                    // The graph()->start() inputs are just placeholders until we computed the
                    // real back-edges and re-structure the control flow so the loop has
                    // exactly two predecessors.
                    current_control = graph()->NewNode(common()->Loop(2), graph()->start(),
                        graph()->start());
                    current_effect = graph()->NewNode(common()->EffectPhi(2), graph()->start(),
                        graph()->start(), current_control);

                    Node* terminate = graph()->NewNode(common()->Terminate(), current_effect,
                        current_control);
                    NodeProperties::MergeControlToEnd(graph(), common(), terminate);
                    loop_headers.push_back(
                        LoopHeader { block, current_control, current_effect });
                } else if (block->PredecessorCount() == 1) {
                    BasicBlock* predecessor = block->PredecessorAt(0);
                    DCHECK_LT(predecessor->rpo_number(), block->rpo_number());
                    current_effect = block_final_effect[predecessor->id().ToSize()];
                    current_control = block_final_control[predecessor->id().ToSize()];
                } else {
                    // Create control merge nodes and effect phis for all predecessor blocks.
                    merge_inputs.clear();
                    effect_phi_inputs.clear();
                    int predecessor_count = static_cast<int>(block->PredecessorCount());
                    for (int i = 0; i < predecessor_count; ++i) {
                        BasicBlock* predecessor = block->PredecessorAt(i);
                        DCHECK_LT(predecessor->rpo_number(), block->rpo_number());
                        merge_inputs.push_back(block_final_control[predecessor->id().ToSize()]);
                        effect_phi_inputs.push_back(
                            block_final_effect[predecessor->id().ToSize()]);
                    }
                    current_control = graph()->NewNode(common()->Merge(predecessor_count),
                        static_cast<int>(merge_inputs.size()),
                        merge_inputs.data());
                    effect_phi_inputs.push_back(current_control);
                    current_effect = graph()->NewNode(
                        common()->EffectPhi(predecessor_count),
                        static_cast<int>(effect_phi_inputs.size()), effect_phi_inputs.data());
                }

                auto update_current_control_and_effect = [&](Node* node) {
                    bool existing_effect_and_control = IrOpcode::IsIfProjectionOpcode(node->opcode()) || IrOpcode::IsPhiOpcode(node->opcode());
                    if (node->op()->EffectInputCount() > 0) {
                        DCHECK_EQ(1, node->op()->EffectInputCount());
                        if (existing_effect_and_control) {
                            NodeProperties::ReplaceEffectInput(node, current_effect);
                        } else {
                            node->AppendInput(graph()->zone(), current_effect);
                        }
                    }
                    if (node->op()->ControlInputCount() > 0) {
                        DCHECK_EQ(1, node->op()->ControlInputCount());
                        if (existing_effect_and_control) {
                            NodeProperties::ReplaceControlInput(node, current_control);
                        } else {
                            node->AppendInput(graph()->zone(), current_control);
                        }
                    }
                    if (node->op()->EffectOutputCount() > 0) {
                        DCHECK_EQ(1, node->op()->EffectOutputCount());
                        current_effect = node;
                    }
                    if (node->op()->ControlOutputCount() > 0) {
                        current_control = node;
                    }
                };

                for (Node* node : *block) {
                    update_current_control_and_effect(node);
                }
                if (block->deferred())
                    MarkControlDeferred(current_control);

                if (Node* block_terminator = block->control_input()) {
                    update_current_control_and_effect(block_terminator);
                }

                block_final_effect[block->id().ToSize()] = current_effect;
                block_final_control[block->id().ToSize()] = current_control;
            }

            // Fix-up loop backedges and re-structure control flow so that loop nodes have
            // exactly two control predecessors.
            for (const LoopHeader& loop_header : loop_headers) {
                BasicBlock* block = loop_header.block;
                std::vector<BasicBlock*> loop_entries;
                std::vector<BasicBlock*> loop_backedges;
                for (size_t i = 0; i < block->PredecessorCount(); ++i) {
                    BasicBlock* predecessor = block->PredecessorAt(i);
                    if (block->LoopContains(predecessor)) {
                        loop_backedges.push_back(predecessor);
                    } else {
                        DCHECK(loop_backedges.empty());
                        loop_entries.push_back(predecessor);
                    }
                }
                DCHECK(!loop_entries.empty());
                DCHECK(!loop_backedges.empty());

                int entrance_count = static_cast<int>(loop_entries.size());
                int backedge_count = static_cast<int>(loop_backedges.size());
                Node* control_loop_entry = CreateNodeFromPredecessors(
                    loop_entries, block_final_control, common()->Merge(entrance_count), {});
                Node* control_backedge = CreateNodeFromPredecessors(loop_backedges, block_final_control,
                    common()->Merge(backedge_count), {});
                Node* effect_loop_entry = CreateNodeFromPredecessors(
                    loop_entries, block_final_effect, common()->EffectPhi(entrance_count),
                    { control_loop_entry });
                Node* effect_backedge = CreateNodeFromPredecessors(
                    loop_backedges, block_final_effect, common()->EffectPhi(backedge_count),
                    { control_backedge });

                loop_header.loop_node->ReplaceInput(0, control_loop_entry);
                loop_header.loop_node->ReplaceInput(1, control_backedge);
                loop_header.effect_phi->ReplaceInput(0, effect_loop_entry);
                loop_header.effect_phi->ReplaceInput(1, effect_backedge);

                for (Node* node : *block) {
                    if (node->opcode() == IrOpcode::kPhi) {
                        MakePhiBinary(node, static_cast<int>(loop_entries.size()),
                            control_loop_entry, control_backedge);
                    }
                }
            }
        }

        Node* RawMachineAssembler::CreateNodeFromPredecessors(
            const std::vector<BasicBlock*>& predecessors,
            const std::vector<Node*>& sidetable, const Operator* op,
            const std::vector<Node*>& additional_inputs)
        {
            if (predecessors.size() == 1) {
                return sidetable[predecessors.front()->id().ToSize()];
            }
            std::vector<Node*> inputs;
            for (BasicBlock* predecessor : predecessors) {
                inputs.push_back(sidetable[predecessor->id().ToSize()]);
            }
            for (Node* additional_input : additional_inputs) {
                inputs.push_back(additional_input);
            }
            return graph()->NewNode(op, static_cast<int>(inputs.size()), inputs.data());
        }

        void RawMachineAssembler::MakePhiBinary(Node* phi, int split_point,
            Node* left_control,
            Node* right_control)
        {
            int value_count = phi->op()->ValueInputCount();
            if (value_count == 2)
                return;
            DCHECK_LT(split_point, value_count);
            DCHECK_GT(split_point, 0);

            MachineRepresentation rep = PhiRepresentationOf(phi->op());
            int left_input_count = split_point;
            int right_input_count = value_count - split_point;

            Node* left_input;
            if (left_input_count == 1) {
                left_input = NodeProperties::GetValueInput(phi, 0);
            } else {
                std::vector<Node*> inputs;
                for (int i = 0; i < left_input_count; ++i) {
                    inputs.push_back(NodeProperties::GetValueInput(phi, i));
                }
                inputs.push_back(left_control);
                left_input = graph()->NewNode(common()->Phi(rep, static_cast<int>(left_input_count)),
                    static_cast<int>(inputs.size()), inputs.data());
            }

            Node* right_input;
            if (right_input_count == 1) {
                right_input = NodeProperties::GetValueInput(phi, split_point);
            } else {
                std::vector<Node*> inputs;
                for (int i = split_point; i < value_count; ++i) {
                    inputs.push_back(NodeProperties::GetValueInput(phi, i));
                }
                inputs.push_back(right_control);
                right_input = graph()->NewNode(
                    common()->Phi(rep, static_cast<int>(right_input_count)),
                    static_cast<int>(inputs.size()), inputs.data());
            }

            Node* control = NodeProperties::GetControlInput(phi);
            phi->TrimInputCount(3);
            phi->ReplaceInput(0, left_input);
            phi->ReplaceInput(1, right_input);
            phi->ReplaceInput(2, control);
            NodeProperties::ChangeOp(phi, common()->Phi(rep, 2));
        }

        void RawMachineAssembler::MarkControlDeferred(Node* control_node)
        {
            BranchHint new_branch_hint;
            Node* responsible_branch = nullptr;
            while (responsible_branch == nullptr) {
                switch (control_node->opcode()) {
                case IrOpcode::kIfException:
                    // IfException projections are deferred by default.
                    return;
                case IrOpcode::kIfSuccess:
                    control_node = NodeProperties::GetControlInput(control_node);
                    continue;
                case IrOpcode::kIfValue: {
                    IfValueParameters parameters = IfValueParametersOf(control_node->op());
                    if (parameters.hint() != BranchHint::kFalse) {
                        NodeProperties::ChangeOp(
                            control_node, common()->IfValue(parameters.value(), parameters.comparison_order(), BranchHint::kFalse));
                    }
                    return;
                }
                case IrOpcode::kIfDefault:
                    if (BranchHintOf(control_node->op()) != BranchHint::kFalse) {
                        NodeProperties::ChangeOp(control_node,
                            common()->IfDefault(BranchHint::kFalse));
                    }
                    return;
                case IrOpcode::kIfTrue: {
                    Node* branch = NodeProperties::GetControlInput(control_node);
                    BranchHint hint = BranchOperatorInfoOf(branch->op()).hint;
                    if (hint == BranchHint::kTrue) {
                        // The other possibility is also deferred, so the responsible branch
                        // has to be before.
                        control_node = NodeProperties::GetControlInput(branch);
                        continue;
                    }
                    new_branch_hint = BranchHint::kFalse;
                    responsible_branch = branch;
                    break;
                }
                case IrOpcode::kIfFalse: {
                    Node* branch = NodeProperties::GetControlInput(control_node);
                    BranchHint hint = BranchOperatorInfoOf(branch->op()).hint;
                    if (hint == BranchHint::kFalse) {
                        // The other possibility is also deferred, so the responsible branch
                        // has to be before.
                        control_node = NodeProperties::GetControlInput(branch);
                        continue;
                    }
                    new_branch_hint = BranchHint::kTrue;
                    responsible_branch = branch;
                    break;
                }
                case IrOpcode::kMerge:
                    for (int i = 0; i < control_node->op()->ControlInputCount(); ++i) {
                        MarkControlDeferred(NodeProperties::GetControlInput(control_node, i));
                    }
                    return;
                case IrOpcode::kLoop:
                    control_node = NodeProperties::GetControlInput(control_node, 0);
                    continue;
                case IrOpcode::kBranch:
                case IrOpcode::kSwitch:
                    UNREACHABLE();
                case IrOpcode::kStart:
                    return;
                default:
                    DCHECK_EQ(1, control_node->op()->ControlInputCount());
                    control_node = NodeProperties::GetControlInput(control_node);
                    continue;
                }
            }

            BranchOperatorInfo info = BranchOperatorInfoOf(responsible_branch->op());
            if (info.hint == new_branch_hint)
                return;
            NodeProperties::ChangeOp(
                responsible_branch,
                common()->Branch(new_branch_hint, info.is_safety_check));
        }

        Node* RawMachineAssembler::TargetParameter()
        {
            DCHECK_NOT_NULL(target_parameter_);
            return target_parameter_;
        }

        Node* RawMachineAssembler::Parameter(size_t index)
        {
            DCHECK_LT(index, parameter_count());
            return parameters_[index];
        }

        void RawMachineAssembler::Goto(RawMachineLabel* label)
        {
            DCHECK(current_block_ != schedule()->end());
            schedule()->AddGoto(CurrentBlock(), Use(label));
            current_block_ = nullptr;
        }

        void RawMachineAssembler::Branch(Node* condition, RawMachineLabel* true_val,
            RawMachineLabel* false_val)
        {
            DCHECK(current_block_ != schedule()->end());
            Node* branch = MakeNode(
                common()->Branch(BranchHint::kNone, IsSafetyCheck::kNoSafetyCheck), 1,
                &condition);
            BasicBlock* true_block = schedule()->NewBasicBlock();
            BasicBlock* false_block = schedule()->NewBasicBlock();
            schedule()->AddBranch(CurrentBlock(), branch, true_block, false_block);

            true_block->AddNode(MakeNode(common()->IfTrue(), 1, &branch));
            schedule()->AddGoto(true_block, Use(true_val));

            false_block->AddNode(MakeNode(common()->IfFalse(), 1, &branch));
            schedule()->AddGoto(false_block, Use(false_val));

            current_block_ = nullptr;
        }

        void RawMachineAssembler::Continuations(Node* call, RawMachineLabel* if_success,
            RawMachineLabel* if_exception)
        {
            DCHECK_NOT_NULL(schedule_);
            DCHECK_NOT_NULL(current_block_);
            schedule()->AddCall(CurrentBlock(), call, Use(if_success), Use(if_exception));
            current_block_ = nullptr;
        }

        void RawMachineAssembler::Switch(Node* index, RawMachineLabel* default_label,
            const int32_t* case_values,
            RawMachineLabel** case_labels,
            size_t case_count)
        {
            DCHECK_NE(schedule()->end(), current_block_);
            size_t succ_count = case_count + 1;
            Node* switch_node = MakeNode(common()->Switch(succ_count), 1, &index);
            BasicBlock** succ_blocks = zone()->NewArray<BasicBlock*>(succ_count);
            for (size_t index = 0; index < case_count; ++index) {
                int32_t case_value = case_values[index];
                BasicBlock* case_block = schedule()->NewBasicBlock();
                Node* case_node = graph()->NewNode(common()->IfValue(case_value), switch_node);
                schedule()->AddNode(case_block, case_node);
                schedule()->AddGoto(case_block, Use(case_labels[index]));
                succ_blocks[index] = case_block;
            }
            BasicBlock* default_block = schedule()->NewBasicBlock();
            Node* default_node = graph()->NewNode(common()->IfDefault(), switch_node);
            schedule()->AddNode(default_block, default_node);
            schedule()->AddGoto(default_block, Use(default_label));
            succ_blocks[case_count] = default_block;
            schedule()->AddSwitch(CurrentBlock(), switch_node, succ_blocks, succ_count);
            current_block_ = nullptr;
        }

        void RawMachineAssembler::Return(Node* value)
        {
            Node* values[] = { Int32Constant(0), value };
            Node* ret = MakeNode(common()->Return(1), 2, values);
            schedule()->AddReturn(CurrentBlock(), ret);
            current_block_ = nullptr;
        }

        void RawMachineAssembler::Return(Node* v1, Node* v2)
        {
            Node* values[] = { Int32Constant(0), v1, v2 };
            Node* ret = MakeNode(common()->Return(2), 3, values);
            schedule()->AddReturn(CurrentBlock(), ret);
            current_block_ = nullptr;
        }

        void RawMachineAssembler::Return(Node* v1, Node* v2, Node* v3)
        {
            Node* values[] = { Int32Constant(0), v1, v2, v3 };
            Node* ret = MakeNode(common()->Return(3), 4, values);
            schedule()->AddReturn(CurrentBlock(), ret);
            current_block_ = nullptr;
        }

        void RawMachineAssembler::Return(Node* v1, Node* v2, Node* v3, Node* v4)
        {
            Node* values[] = { Int32Constant(0), v1, v2, v3, v4 };
            Node* ret = MakeNode(common()->Return(4), 5, values);
            schedule()->AddReturn(CurrentBlock(), ret);
            current_block_ = nullptr;
        }

        void RawMachineAssembler::Return(int count, Node* vs[])
        {
            using Node_ptr = Node*;
            Node** values = new Node_ptr[count + 1];
            values[0] = Int32Constant(0);
            for (int i = 0; i < count; ++i)
                values[i + 1] = vs[i];
            Node* ret = MakeNode(common()->Return(count), count + 1, values);
            schedule()->AddReturn(CurrentBlock(), ret);
            current_block_ = nullptr;
            delete[] values;
        }

        void RawMachineAssembler::PopAndReturn(Node* pop, Node* value)
        {
            Node* values[] = { pop, value };
            Node* ret = MakeNode(common()->Return(1), 2, values);
            schedule()->AddReturn(CurrentBlock(), ret);
            current_block_ = nullptr;
        }

        void RawMachineAssembler::PopAndReturn(Node* pop, Node* v1, Node* v2)
        {
            Node* values[] = { pop, v1, v2 };
            Node* ret = MakeNode(common()->Return(2), 3, values);
            schedule()->AddReturn(CurrentBlock(), ret);
            current_block_ = nullptr;
        }

        void RawMachineAssembler::PopAndReturn(Node* pop, Node* v1, Node* v2,
            Node* v3)
        {
            Node* values[] = { pop, v1, v2, v3 };
            Node* ret = MakeNode(common()->Return(3), 4, values);
            schedule()->AddReturn(CurrentBlock(), ret);
            current_block_ = nullptr;
        }

        void RawMachineAssembler::PopAndReturn(Node* pop, Node* v1, Node* v2, Node* v3,
            Node* v4)
        {
            Node* values[] = { pop, v1, v2, v3, v4 };
            Node* ret = MakeNode(common()->Return(4), 5, values);
            schedule()->AddReturn(CurrentBlock(), ret);
            current_block_ = nullptr;
        }

        void RawMachineAssembler::DebugAbort(Node* message)
        {
            AddNode(machine()->DebugAbort(), message);
        }

        void RawMachineAssembler::DebugBreak() { AddNode(machine()->DebugBreak()); }

        void RawMachineAssembler::Unreachable()
        {
            Node* ret = MakeNode(common()->Throw(), 0, nullptr);
            schedule()->AddThrow(CurrentBlock(), ret);
            current_block_ = nullptr;
        }

        void RawMachineAssembler::Comment(const std::string& msg)
        {
            size_t length = msg.length() + 1;
            char* zone_buffer = zone()->NewArray<char>(length);
            MemCopy(zone_buffer, msg.c_str(), length);
            AddNode(machine()->Comment(zone_buffer));
        }

        Node* RawMachineAssembler::CallN(CallDescriptor* call_descriptor,
            int input_count, Node* const* inputs)
        {
            DCHECK(!call_descriptor->NeedsFrameState());
            // +1 is for target.
            DCHECK_EQ(input_count, call_descriptor->ParameterCount() + 1);
            return AddNode(common()->Call(call_descriptor), input_count, inputs);
        }

        Node* RawMachineAssembler::CallNWithFrameState(CallDescriptor* call_descriptor,
            int input_count,
            Node* const* inputs)
        {
            DCHECK(call_descriptor->NeedsFrameState());
            // +2 is for target and frame state.
            DCHECK_EQ(input_count, call_descriptor->ParameterCount() + 2);
            return AddNode(common()->Call(call_descriptor), input_count, inputs);
        }

        Node* RawMachineAssembler::TailCallN(CallDescriptor* call_descriptor,
            int input_count, Node* const* inputs)
        {
            // +1 is for target.
            DCHECK_EQ(input_count, call_descriptor->ParameterCount() + 1);
            Node* tail_call = MakeNode(common()->TailCall(call_descriptor), input_count, inputs);
            schedule()->AddTailCall(CurrentBlock(), tail_call);
            current_block_ = nullptr;
            return tail_call;
        }

        namespace {

            Node* CallCFunctionImpl(
                RawMachineAssembler* rasm, Node* function, MachineType return_type,
                std::initializer_list<RawMachineAssembler::CFunctionArg> args,
                bool caller_saved_regs, SaveFPRegsMode mode)
            {
                static constexpr std::size_t kNumCArgs = 10;

                MachineSignature::Builder builder(rasm->zone(), 1, args.size());
                builder.AddReturn(return_type);
                for (const auto& arg : args)
                    builder.AddParam(arg.first);

                auto call_descriptor = Linkage::GetSimplifiedCDescriptor(rasm->zone(), builder.Build());

                if (caller_saved_regs)
                    call_descriptor->set_save_fp_mode(mode);

                base::SmallVector<Node*, kNumCArgs> nodes(args.size() + 1);
                nodes[0] = function;
                std::transform(
                    args.begin(), args.end(), std::next(nodes.begin()),
                    [](const RawMachineAssembler::CFunctionArg& arg) { return arg.second; });

                auto common = rasm->common();
                return rasm->AddNode(
                    caller_saved_regs ? common->CallWithCallerSavedRegisters(call_descriptor)
                                      : common->Call(call_descriptor),
                    static_cast<int>(nodes.size()), nodes.begin());
            }

        } // namespace

        Node* RawMachineAssembler::CallCFunction(
            Node* function, MachineType return_type,
            std::initializer_list<RawMachineAssembler::CFunctionArg> args)
        {
            return CallCFunctionImpl(this, function, return_type, args, false,
                kDontSaveFPRegs);
        }

        Node* RawMachineAssembler::CallCFunctionWithCallerSavedRegisters(
            Node* function, MachineType return_type, SaveFPRegsMode mode,
            std::initializer_list<RawMachineAssembler::CFunctionArg> args)
        {
            return CallCFunctionImpl(this, function, return_type, args, true, mode);
        }

        BasicBlock* RawMachineAssembler::Use(RawMachineLabel* label)
        {
            label->used_ = true;
            return EnsureBlock(label);
        }

        BasicBlock* RawMachineAssembler::EnsureBlock(RawMachineLabel* label)
        {
            if (label->block_ == nullptr) {
                label->block_ = schedule()->NewBasicBlock();
            }
            return label->block_;
        }

        void RawMachineAssembler::Bind(RawMachineLabel* label)
        {
            DCHECK_NULL(current_block_);
            DCHECK(!label->bound_);
            label->bound_ = true;
            current_block_ = EnsureBlock(label);
            current_block_->set_deferred(label->deferred_);
        }

#if DEBUG
        void RawMachineAssembler::Bind(RawMachineLabel* label,
            AssemblerDebugInfo info)
        {
            if (current_block_ != nullptr) {
                std::stringstream str;
                str << "Binding label without closing previous block:"
                    << "\n#    label:          " << info
                    << "\n#    previous block: " << *current_block_;
                FATAL("%s", str.str().c_str());
            }
            Bind(label);
            current_block_->set_debug_info(info);
        }

        void RawMachineAssembler::PrintCurrentBlock(std::ostream& os)
        {
            os << CurrentBlock();
        }

        void RawMachineAssembler::SetInitialDebugInformation(
            AssemblerDebugInfo debug_info)
        {
            CurrentBlock()->set_debug_info(debug_info);
        }
#endif // DEBUG

        bool RawMachineAssembler::InsideBlock()
        {
            return current_block_ != nullptr;
        }

        BasicBlock* RawMachineAssembler::CurrentBlock()
        {
            DCHECK(current_block_);
            return current_block_;
        }

        Node* RawMachineAssembler::Phi(MachineRepresentation rep, int input_count,
            Node* const* inputs)
        {
            Node** buffer = new (zone()->New(sizeof(Node*) * (input_count + 1)))
                Node*[input_count + 1];
            std::copy(inputs, inputs + input_count, buffer);
            buffer[input_count] = graph()->start();
            return AddNode(common()->Phi(rep, input_count), input_count + 1, buffer);
        }

        void RawMachineAssembler::AppendPhiInput(Node* phi, Node* new_input)
        {
            const Operator* op = phi->op();
            const Operator* new_op = common()->ResizeMergeOrPhi(op, phi->InputCount());
            phi->InsertInput(zone(), phi->InputCount() - 1, new_input);
            NodeProperties::ChangeOp(phi, new_op);
        }

        Node* RawMachineAssembler::AddNode(const Operator* op, int input_count,
            Node* const* inputs)
        {
            DCHECK_NOT_NULL(schedule_);
            DCHECK_NOT_NULL(current_block_);
            Node* node = MakeNode(op, input_count, inputs);
            schedule()->AddNode(CurrentBlock(), node);
            return node;
        }

        Node* RawMachineAssembler::MakeNode(const Operator* op, int input_count,
            Node* const* inputs)
        {
            // The raw machine assembler nodes do not have effect and control inputs,
            // so we disable checking input counts here.
            return graph()->NewNodeUnchecked(op, input_count, inputs);
        }

        RawMachineLabel::~RawMachineLabel()
        {
#if DEBUG
            if (bound_ == used_)
                return;
            std::stringstream str;
            if (bound_) {
                str << "A label has been bound but it's not used."
                    << "\n#    label: " << *block_;
            } else {
                str << "A label has been used but it's not bound.";
            }
            FATAL("%s", str.str().c_str());
#endif // DEBUG
        }

    } // namespace compiler
} // namespace internal
} // namespace v8
